home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / athena / misc / movemail.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-08-05  |  15.3 KB  |  732 lines

  1. /* movemail foo bar -- move file foo to file bar,
  2.    locking file foo the way /bin/mail respects.
  3.    Copyright (C) 1986 Free Software Foundation, Inc.
  4.  
  5. This file is part of GNU Emacs.
  6.  
  7. GNU Emacs is free software; you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation; either version 1, or (at your option)
  10. any later version.
  11.  
  12. GNU Emacs is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. GNU General Public License for more details.
  16.  
  17. You should have received a copy of the GNU General Public License
  18. along with GNU Emacs; see the file COPYING.  If not, write to
  19. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  20.  
  21. /*
  22.  * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
  23.  *
  24.  * Added POP (Post Office Protocol) service.  When compiled -DPOP
  25.  * movemail will accept input filename arguments of the form
  26.  * "po:username".  This will cause movemail to open a connection to
  27.  * a pop server running on $MAILHOST (environment variable).  Movemail
  28.  * must be setuid to root in order to work with POP.
  29.  * 
  30.  * New module: popmail.c
  31.  * Modified routines:
  32.  *    main - added code within #ifdef MAIL_USE_POP; added setuid(getuid())
  33.  *        after POP code. 
  34.  * New routines in movemail.c:
  35.  *    get_errmsg - return pointer to system error message
  36.  *
  37.  * Modified November, 1990 by Jonathan I. Kamens (Project Athena)
  38.  *
  39.  * Added KPOP (Kerberized POP) service to POP code.  If KERBEROS is
  40.  * defined, then:
  41.  *
  42.  * 1. The "kpop" service is used instead of the "pop" service.
  43.  * 2. Kerberos authorization data is sent to the server upon start-up.
  44.  * 3. Instead of sending USER and RPOP, USER and PASS are sent, both
  45.  *    containing the username of the user retrieving mail.
  46.  *
  47.  * Added HESIOD support.  If HESIOD is defined, then an attempt will
  48.  * be made to look up the user's mailhost in the hesiod nameserver
  49.  * database if the MAILHOST environment variable is not set.
  50.  * 
  51.  */
  52.  
  53. #include <sys/types.h>
  54. #include <sys/stat.h>
  55. #include <sys/file.h>
  56. #include <errno.h>
  57. #define NO_SHORTNAMES   /* Tell config not to load remap.h */
  58. #include "../src/config.h"
  59.  
  60. #ifdef USG
  61. #include <fcntl.h>
  62. #include <unistd.h>
  63. #ifndef F_OK
  64. #define F_OK 0
  65. #define X_OK 1
  66. #define W_OK 2
  67. #define R_OK 4
  68. #endif
  69. #endif /* USG */
  70.  
  71. #ifdef XENIX
  72. #include <sys/locking.h>
  73. #endif
  74.  
  75. /* Cancel substitutions made by config.h for Emacs.  */
  76. #undef open
  77. #undef read
  78. #undef write
  79. #undef close
  80.  
  81. char *concat ();
  82. extern int errno;
  83.  
  84. /* Nonzero means this is name of a lock file to delete on fatal error.  */
  85. char *delete_lockname;
  86.  
  87. main (argc, argv)
  88.      int argc;
  89.      char **argv;
  90. {
  91.   char *inname, *outname;
  92.   int indesc, outdesc;
  93.   char buf[1024];
  94.   int nread;
  95.  
  96. #ifndef MAIL_USE_FLOCK
  97.   struct stat st;
  98.   long now;
  99.   int tem;
  100.   char *lockname, *p;
  101.   char tempname[40];
  102.   int desc;
  103. #endif /* not MAIL_USE_FLOCK */
  104.  
  105.   delete_lockname = 0;
  106.  
  107.   if (argc < 3)
  108.     fatal ("two arguments required");
  109.  
  110.   inname = argv[1];
  111.   outname = argv[2];
  112.  
  113.   /* Check access to output file.  */
  114.   if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
  115.     pfatal_with_name (outname);
  116.  
  117.   /* Also check that outname's directory is writeable to the real uid.  */
  118.   {
  119.     char *buf = (char *) malloc (strlen (outname) + 1);
  120.     char *p;
  121.     strcpy (buf, outname);
  122.     p = buf + strlen (buf);
  123.     while (p > buf && p[-1] != '/')
  124.       *--p = 0;
  125.     if (p == buf)
  126.       *p++ = '.';
  127.     if (access (buf, W_OK) != 0)
  128.       pfatal_with_name (buf);
  129.     free (buf);
  130.   }
  131.  
  132. #ifdef MAIL_USE_POP
  133.   if (!bcmp (inname, "po:", 3))
  134.     {
  135.       int status; char *user;
  136.  
  137.       user = (char *) rindex (inname, ':') + 1;
  138.       status = popmail (user, outname);
  139.       exit (status);
  140.     }
  141.  
  142.   setuid (getuid());
  143. #endif /* MAIL_USE_POP */
  144.  
  145.   /* Check access to input file.  */
  146.   if (access (inname, R_OK | W_OK) != 0)
  147.     pfatal_with_name (inname);
  148.  
  149. #ifndef MAIL_USE_FLOCK
  150.   /* Use a lock file named /usr/spool/mail/$USER.lock:
  151.      If it exists, the mail file is locked.  */
  152.   lockname = concat (inname, ".lock", "");
  153.   strcpy (tempname, inname);
  154.   p = tempname + strlen (tempname);
  155.   while (p != tempname && p[-1] != '/')
  156.     p--;
  157.   *p = 0;
  158.   strcpy (p, "EXXXXXX");
  159.   mktemp (tempname);
  160.   (void) unlink (tempname);
  161.  
  162.   while (1)
  163.     {
  164.       /* Create the lock file, but not under the lock file name.  */
  165.       /* Give up if cannot do that.  */
  166.       desc = open (tempname, O_WRONLY | O_CREAT, 0666);
  167.       if (desc < 0)
  168.         pfatal_with_name (concat ("temporary file \"", tempname, "\""));
  169.       close (desc);
  170.  
  171.       tem = link (tempname, lockname);
  172.       (void) unlink (tempname);
  173.       if (tem >= 0)
  174.     break;
  175.       sleep (1);
  176.  
  177.       /* If lock file is a minute old, unlock it.  */
  178.       if (stat (lockname, &st) >= 0)
  179.     {
  180.       now = time (0);
  181.       if (st.st_ctime < now - 60)
  182.         (void) unlink (lockname);
  183.     }
  184.     }
  185.  
  186.   delete_lockname = lockname;
  187. #endif /* not MAIL_USE_FLOCK */
  188.  
  189. #ifdef MAIL_USE_FLOCK
  190.   indesc = open (inname, O_RDWR);
  191. #else /* if not MAIL_USE_FLOCK */
  192.   indesc = open (inname, O_RDONLY);
  193. #endif /* not MAIL_USE_FLOCK */
  194.   if (indesc < 0)
  195.     pfatal_with_name (inname);
  196.  
  197. #if defined(BSD) || defined(XENIX)
  198.   /* In case movemail is setuid to root, make sure the user can
  199.      read the output file.  */
  200.   /* This is desirable for all systems
  201.      but I don't want to assume all have the umask system call */
  202.   umask (umask (0) & 0333);
  203. #endif /* BSD or Xenix */
  204.   outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
  205.   if (outdesc < 0)
  206.     pfatal_with_name (outname);
  207. #ifdef MAIL_USE_FLOCK
  208. #ifdef XENIX
  209.   if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
  210. #else
  211.   flock (indesc, LOCK_EX);
  212. #endif
  213. #endif /* MAIL_USE_FLOCK */
  214.  
  215.   while (1)
  216.     {
  217.       nread = read (indesc, buf, sizeof buf);
  218.       if (nread != write (outdesc, buf, nread))
  219.     {
  220.       int saved_errno = errno;
  221.       (void) unlink (outname);
  222.       errno = saved_errno;
  223.       pfatal_with_name (outname);
  224.     }
  225.       if (nread < sizeof buf)
  226.     break;
  227.     }
  228.  
  229. #ifdef BSD
  230.   fsync (outdesc);
  231. #endif
  232.  
  233.   /* Check to make sure no errors before we zap the inbox.  */
  234.   if (close (outdesc) != 0)
  235.     {
  236.       int saved_errno = errno;
  237.       (void) unlink (outname);
  238.       errno = saved_errno;
  239.       pfatal_with_name (outname);
  240.   }
  241.  
  242. #ifdef MAIL_USE_FLOCK
  243. #if defined(STRIDE) || defined(XENIX)
  244.   /* Stride, xenix have file locking, but no ftruncate.  This mess will do. */
  245.   (void) close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
  246. #else
  247.   (void) ftruncate (indesc, 0L);
  248. #endif /* STRIDE or XENIX */
  249. #endif /* MAIL_USE_FLOCK */
  250.   close (indesc);
  251.  
  252. #ifndef MAIL_USE_FLOCK
  253.   /* Delete the input file; if we can't, at least get rid of its contents.  */
  254.   if (unlink (inname) < 0)
  255.     if (errno != ENOENT)
  256.       creat (inname, 0666);
  257.   (void) unlink (lockname);
  258. #endif /* not MAIL_USE_FLOCK */
  259.   exit (0);
  260. }
  261.  
  262. /* Print error message and exit.  */
  263.  
  264. fatal (s1, s2)
  265.      char *s1, *s2;
  266. {
  267.   if (delete_lockname)
  268.     unlink (delete_lockname);
  269.   error (s1, s2);
  270.   exit (1);
  271. }
  272.  
  273. /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
  274.  
  275. error (s1, s2, s3)
  276.      char *s1, *s2, *s3;
  277. {
  278.   printf ("movemail: ");
  279.   printf (s1, s2, s3);
  280.   printf ("\n");
  281. }
  282.  
  283. pfatal_with_name (name)
  284.      char *name;
  285. {
  286.   extern int errno, sys_nerr;
  287.   extern char *sys_errlist[];
  288.   char *s;
  289.  
  290.   if (errno < sys_nerr)
  291.     s = concat ("", sys_errlist[errno], " for %s");
  292.   else
  293.     s = "cannot open %s";
  294.   fatal (s, name);
  295. }
  296.  
  297. /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3.  */
  298.  
  299. char *
  300. concat (s1, s2, s3)
  301.      char *s1, *s2, *s3;
  302. {
  303.   int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
  304.   char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
  305.  
  306.   strcpy (result, s1);
  307.   strcpy (result + len1, s2);
  308.   strcpy (result + len1 + len2, s3);
  309.   *(result + len1 + len2 + len3) = 0;
  310.  
  311.   return result;
  312. }
  313.  
  314. /* Like malloc but get fatal error if memory is exhausted.  */
  315.  
  316. int
  317. xmalloc (size)
  318.      int size;
  319. {
  320.   int result = malloc (size);
  321.   if (!result)
  322.     fatal ("virtual memory exhausted", 0);
  323.   return result;
  324. }
  325.  
  326. /* This is the guts of the interface to the Post Office Protocol.  */
  327.  
  328. #ifdef MAIL_USE_POP
  329.  
  330. #include <sys/socket.h>
  331. #include <netinet/in.h>
  332. #include <netdb.h>
  333. #include <stdio.h>
  334. #ifdef KERBEROS
  335. #include <krb.h>
  336. #include <des.h>
  337. #endif
  338. #ifdef HESIOD
  339. #include <hesiod.h>
  340. #endif
  341.  
  342. #ifdef USG
  343. #include <fcntl.h>
  344. /* Cancel substitutions made by config.h for Emacs.  */
  345. #undef open
  346. #undef read
  347. #undef write
  348. #undef close
  349. #endif /* USG */
  350.  
  351. #define NOTOK (-1)
  352. #define OK 0
  353. #define DONE 1
  354.  
  355. char *progname;
  356. FILE *sfi;
  357. FILE *sfo;
  358. char ibuffer[BUFSIZ];
  359. char obuffer[BUFSIZ];
  360. char Errmsg[80];
  361.  
  362. static int debug = 0;
  363.  
  364. popmail(user, outfile)
  365. char *user;
  366. char *outfile;
  367. {
  368.     char *host;
  369.     int nmsgs, nbytes;
  370.     char response[128];
  371.     register int i;
  372.     int mbfi;
  373.     FILE *mbf;
  374.     char *getenv();
  375.     int mbx_write();
  376.     char *get_errmsg();
  377. #ifdef HESIOD
  378.     struct hes_postoffice *p;
  379. #endif
  380.     
  381.     host = getenv("MAILHOST");
  382. #ifdef HESIOD
  383.     if (host == NULL) {
  384.      p = hes_getmailhost(user);
  385.      if (p != NULL && strcmp(p->po_type, "POP") == 0)
  386.           host = p->po_host;
  387.      else
  388.           fatal("no POP server listed in Hesiod");
  389.     }
  390. #endif /* HESIOD */
  391.     if (host == NULL) {
  392.     fatal("no MAILHOST defined");
  393.     }
  394.  
  395.     if (pop_init(host) == NOTOK) {
  396.     error(Errmsg);
  397.     return(1);
  398.     }
  399.  
  400.     if ((getline(response, sizeof response, sfi) != OK) || (*response != '+')) {
  401.     error(response);
  402.     return(1);
  403.     }
  404.  
  405. #ifdef KERBEROS
  406.     if (pop_command("USER %s", user) == NOTOK || 
  407.     pop_command("PASS %s", user) == NOTOK)
  408. #else
  409.     if (pop_command("USER %s", user) == NOTOK || 
  410.     pop_command("RPOP %s", user) == NOTOK)
  411. #endif
  412.     {
  413.     error(Errmsg);
  414.     pop_command("QUIT");
  415.     return(1);
  416.     }
  417.  
  418.     if (pop_stat(&nmsgs, &nbytes) == NOTOK) {
  419.     error(Errmsg);
  420.     pop_command("QUIT");
  421.     return(1);
  422.     }
  423.  
  424.     if (!nmsgs)
  425.       {
  426.     pop_command("QUIT");
  427.     return(0);
  428.       }
  429.  
  430.     mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
  431.     if (mbfi < 0)
  432.       {
  433.     pop_command("QUIT");
  434.     error("Error in open: %s, %s", get_errmsg(), outfile);
  435.     return(1);
  436.       }
  437.     fchown(mbfi, getuid(), -1);
  438.  
  439.     if ((mbf = fdopen(mbfi, "w")) == NULL)
  440.       {
  441.     pop_command("QUIT");
  442.     error("Error in fdopen: %s", get_errmsg());
  443.     close(mbfi);
  444.     unlink(outfile);
  445.     return(1);
  446.       }
  447.  
  448.     for (i = 1; i <= nmsgs; i++) {
  449.     mbx_delimit_begin(mbf);
  450.     if (pop_retr(i, mbx_write, mbf) != OK) {
  451.         error(Errmsg);
  452.         pop_command("QUIT");
  453.         close(mbfi);
  454.         return(1);
  455.     }
  456.     mbx_delimit_end(mbf);
  457.     fflush(mbf);
  458.     }
  459.  
  460.     for (i = 1; i <= nmsgs; i++) {
  461.     if (pop_command("DELE %d", i) == NOTOK) {
  462.         error(Errmsg);
  463.         pop_command("QUIT");
  464.         close(mbfi);
  465.         return(1);
  466.     }
  467.     }
  468.  
  469.     pop_command("QUIT");
  470.     close(mbfi);
  471.     return(0);
  472. }
  473.  
  474. pop_init(host)
  475. char *host;
  476. {
  477.     register struct hostent *hp;
  478.     register struct servent *sp;
  479.     int lport = IPPORT_RESERVED - 1;
  480.     struct sockaddr_in sin;
  481.     register int s;
  482.     char *get_errmsg();
  483. #ifdef KERBEROS
  484.     KTEXT ticket;
  485.     MSG_DAT msg_data;
  486.     CREDENTIALS cred;
  487.     Key_schedule schedule;
  488.     int rem;
  489. #endif
  490.     
  491.     hp = gethostbyname(host);
  492.     if (hp == NULL) {
  493.     sprintf(Errmsg, "MAILHOST unknown: %s", host);
  494.     return(NOTOK);
  495.     }
  496.  
  497. #ifdef KERBEROS
  498.     sp = getservbyname("kpop", "tcp");
  499. #else
  500.     sp = getservbyname("pop", "tcp");
  501. #endif
  502.     if (sp == 0) {
  503. #ifdef KERBEROS
  504.     strcpy(Errmsg, "tcp/kpop: unknown service");
  505. #else
  506.     strcpy(Errmsg, "tcp/pop: unknown service");
  507. #endif
  508.     return(NOTOK);
  509.     }
  510.  
  511.     sin.sin_family = hp->h_addrtype;
  512.     bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
  513.     sin.sin_port = sp->s_port;
  514. #ifdef KERBEROS
  515.     s = socket(AF_INET, SOCK_STREAM, 0);
  516. #else
  517.     s = rresvport(&lport);
  518. #endif
  519.  
  520.     if (s < 0) {
  521.     sprintf(Errmsg, "error creating socket: %s", get_errmsg());
  522.     return(NOTOK);
  523.     }
  524.  
  525.     if (connect(s, (char *)&sin, sizeof sin) < 0) {
  526.     sprintf(Errmsg, "error during connect: %s", get_errmsg());
  527.     close(s);
  528.     return(NOTOK);
  529.     }
  530.  
  531. #ifdef KERBEROS
  532.     ticket = (KTEXT) malloc(sizeof(KTEXT_ST));
  533.     rem = krb_sendauth(0L, s, ticket, "pop", hp->h_name,
  534. #ifdef _AUX_SOURCE /* krb_realmofhost is broken on AUX */
  535.                KRB_REALM,
  536. #else
  537.                (char *) krb_realmofhost(hp->h_name),
  538. #endif /* _AUX_SOURCE */
  539.                (unsigned long)0, &msg_data, &cred, schedule,
  540.                (struct sockaddr_in *)0,
  541.                (struct sockaddr_in *)0,
  542.                "KPOPV0.1");
  543.     if (rem != KSUCCESS) {
  544.      sprintf(Errmsg, "kerberos error: %s", krb_err_txt[rem]);
  545.      close(s);
  546.      return(NOTOK);
  547.     }
  548. #endif /* KERBEROS */
  549.                
  550.     sfi = fdopen(s, "r");
  551.     sfo = fdopen(s, "w");
  552.     setbuf(sfi,ibuffer);
  553.     setbuf(sfo,obuffer);
  554.     if (sfi == NULL || sfo == NULL) {
  555.     sprintf(Errmsg, "error in fdopen: %s", get_errmsg());
  556.     close(s);
  557.     return(NOTOK);
  558.     }
  559.  
  560.     return(OK);
  561. }
  562.  
  563. pop_command(fmt, a, b, c, d)
  564. char *fmt;
  565. {
  566.     char buf[128];
  567.  
  568.     sprintf(buf, fmt, a, b, c, d);
  569.  
  570.     if (debug) fprintf(stderr, "---> %s\n", buf);
  571.     if (putline(buf, Errmsg, sfo) == NOTOK) return(NOTOK);
  572.  
  573.     if (getline(buf, sizeof buf, sfi) != OK) {
  574.     strcpy(Errmsg, buf);
  575.     return(NOTOK);
  576.     }
  577.  
  578.     if (debug) fprintf(stderr, "<--- %s\n", buf);
  579.     if (*buf != '+') {
  580.     strcpy(Errmsg, buf);
  581.     return(NOTOK);
  582.     } else {
  583.     return(OK);
  584.     }
  585. }
  586.  
  587.     
  588. pop_stat(nmsgs, nbytes)
  589. int *nmsgs, *nbytes;
  590. {
  591.     char buf[128];
  592.  
  593.     if (debug) fprintf(stderr, "---> STAT\n");
  594.     if (putline("STAT", Errmsg, sfo) == NOTOK) return(NOTOK);
  595.  
  596.     if (getline(buf, sizeof buf, sfi) != OK) {
  597.     strcpy(Errmsg, buf);
  598.     return(NOTOK);
  599.     }
  600.  
  601.     if (debug) fprintf(stderr, "<--- %s\n", buf);
  602.     if (*buf != '+') {
  603.     strcpy(Errmsg, buf);
  604.     return(NOTOK);
  605.     } else {
  606.     sscanf(buf, "+OK %d %d", nmsgs, nbytes);
  607.     return(OK);
  608.     }
  609. }
  610.  
  611. pop_retr(msgno, action, arg)
  612. int (*action)();
  613. {
  614.     char buf[128];
  615.  
  616.     sprintf(buf, "RETR %d", msgno);
  617.     if (debug) fprintf(stderr, "%s\n", buf);
  618.     if (putline(buf, Errmsg, sfo) == NOTOK) return(NOTOK);
  619.  
  620.     if (getline(buf, sizeof buf, sfi) != OK) {
  621.     strcpy(Errmsg, buf);
  622.     return(NOTOK);
  623.     }
  624.  
  625.     while (1) {
  626.     switch (multiline(buf, sizeof buf, sfi)) {
  627.     case OK:
  628.         (*action)(buf, arg);
  629.         break;
  630.     case DONE:
  631.         return (OK);
  632.     case NOTOK:
  633.         strcpy(Errmsg, buf);
  634.         return (NOTOK);
  635.     }
  636.     }
  637. }
  638.  
  639. getline(buf, n, f)
  640. char *buf;
  641. register int n;
  642. FILE *f;
  643. {
  644.     register char *p;
  645.     int c;
  646.  
  647.     p = buf;
  648.     while (--n > 0 && (c = fgetc(f)) != EOF)
  649.       if ((*p++ = c) == '\n') break;
  650.  
  651.     if (ferror(f)) {
  652.     strcpy(buf, "error on connection");
  653.     return (NOTOK);
  654.     }
  655.  
  656.     if (c == EOF && p == buf) {
  657.     strcpy(buf, "connection closed by foreign host");
  658.     return (DONE);
  659.     }
  660.  
  661.     *p = NULL;
  662.     if (*--p == '\n') *p = NULL;
  663.     if (*--p == '\r') *p = NULL;
  664.     return(OK);
  665. }
  666.  
  667. multiline(buf, n, f)
  668. char *buf;
  669. register int n;
  670. FILE *f;
  671. {
  672.     if (getline(buf, n, f) != OK) return (NOTOK);
  673.     if (*buf == '.') {
  674.     if (*(buf+1) == NULL) {
  675.         return (DONE);
  676.     } else {
  677.         strcpy(buf, buf+1);
  678.     }
  679.     }
  680.     return(OK);
  681. }
  682.  
  683. char *
  684. get_errmsg()
  685. {
  686.     extern int errno, sys_nerr;
  687.     extern char *sys_errlist[];
  688.     char *s;
  689.  
  690.     if (errno < sys_nerr)
  691.       s = sys_errlist[errno];
  692.     else
  693.       s = "unknown error";
  694.     return(s);
  695. }
  696.  
  697. putline(buf, err, f)
  698. char *buf;
  699. char *err;
  700. FILE *f;
  701. {
  702.     fprintf(f, "%s\r\n", buf);
  703.     fflush(f);
  704.     if (ferror(f)) {
  705.     strcpy(err, "lost connection");
  706.     return(NOTOK);
  707.     }
  708.     return(OK);
  709. }
  710.  
  711. mbx_write(line, mbf)
  712. char *line;
  713. FILE *mbf;
  714. {
  715.     fputs(line, mbf);
  716.     fputc(0x0a, mbf);
  717. }
  718.  
  719. mbx_delimit_begin(mbf)
  720. FILE *mbf;
  721. {
  722.     fputs("\f\n0, unseen,,\n", mbf);
  723. }
  724.  
  725. mbx_delimit_end(mbf)
  726. FILE *mbf;
  727. {
  728.     putc('\037', mbf);
  729. }
  730.  
  731. #endif /* MAIL_USE_POP */
  732.